<template>
  <Teleport to="body">
    <div v-if="!noHead" class="top-toolbar">
      <slot name="filter">
        <data-filter :items="columnArray" @action="showSortDialog = true" @on-query-filter="onQueryFilter" />
      </slot>
      <slot name="sort">
        <data-sorter v-model="showSortDialog" :items="columnArray" @on-sort-data="onSortData" />
      </slot>
    </div>
  </Teleport>
  <div v-if="!isAssociation && !noHead" class="top-toolbar-holder"></div>
  <van-tabs
    :key="tabKey"
    ref="tabRef"
    v-model:active="_active"
    :show-header="isNotEmpty(tabs)"
    shrink
    @click-tab="(arg) => emit('onTabClick', arg)"
  >
    <van-tab
      v-for="tab in isNotEmpty(tabs) ? tabs : [{ id: '0', name: '' }]"
      :key="tab[tabIdField]"
      :name="tab[tabIdField]"
      :title="tab[tabNameField]"
    >
      <empty-content v-if="isEmpty(dataList)" :description="emptyContent" />
      <van-list
        v-else
        :id="tableId"
        v-model:loading="loading"
        :finished="finished"
        :immediate-check="false"
        finished-text="没有更多了"
        @load="onLoad"
      >
        <drag
          :disabled="isEmpty(config.orderColumn) || !draggable"
          :force-fallback="true"
          :item-key="config.idColumn"
          :list="dataList"
          :scroll="true"
          :scroll-sensitivity="150"
          :scroll-speed="10"
          animation="300"
          chosen-class="chosen"
          ghost-class="ghost"
          handle=".card-title"
          @end="onDragEnd"
          @start="() => refreshStore.disableRefresh()"
        >
          <template #item="{ element }">
            <slot :element="element" name="body">
              <van-swipe-cell style="padding-bottom: 6px">
                <data-card
                  :content-item-list="
                    getContentItemList(element, showPositionMap, columnsArrayMap, props.coverPropertyList)
                  "
                  :corner="getCardBadge(element, showPositionMap, columnsArrayMap, columnsReferenceDictMap)"
                  :left-bottom-img="getCardLeftBottomImg(element, showPositionMap, allStaffMap)"
                  :left-bottom-text="getCardLeftBottomText(element, showPositionMap, allStaffMap, columnsArrayMap)"
                  :right-bottom-text="getCardRightBottomText(element, showPositionMap, columnsArrayMap)"
                  :sub-title="getCardSubTitle(element, showPositionMap)"
                  :tags="getCardTags(element, showPositionMap)"
                  :title="getCardTitle(element, config.nameColumn, showPositionMap)"
                  @click="emit('click', element, originalDataMap.get(element[config.idColumn]))"
                >
                  <template #title="{ title }">
                    <slot :title="title" name="title"></slot>
                  </template>
                  <template #subTitle="{ subTitle }">
                    <slot :title="subTitle" name="subTitle"></slot>
                  </template>
                </data-card>
                <template #left>
                  <van-button
                    v-if="detailInfo"
                    class="view-associations-btn"
                    icon="info-o"
                    square
                    type="primary"
                    @click="onclickCardEvent(element)"
                  />
                  <van-button
                    v-if="isNotEmpty(_bindTabs) && associationInfo"
                    class="view-associations-btn"
                    square
                    text="查看关联"
                    type="primary"
                    @click="onViewAssociation(element)"
                  />
                </template>
                <template #right>
                  <slot
                    :data="originalDataMap.get(element[config.idColumn])"
                    :record="element"
                    name="action"
                  ></slot>
                </template>
              </van-swipe-cell>
            </slot>
          </template>
        </drag>
      </van-list>
    </van-tab>
  </van-tabs>
  <Dialog v-model:show="showDetail" title="详细信息" z-index="9999">
    <template #dialog-body>
      <div style="padding: 0 5vw">
        <record-list
          v-for="(detailData, index) in detailDataList"
          :key="index"
          :data-list="detailData.dataList"
          :title="detailData.title"
        />
      </div>
    </template>
  </Dialog>
  <association-portal v-model:show="config.modal.show" :bind-tabs="_bindTabs" :config="config" />
</template>

<script lang="ts" setup>
import { useDictStore, useTreeStore } from '@/framework/store/common'
import { generalQuery, updateOrder } from '@/framework/apis/portal'
import Dialog from '@/framework/components/Dialog/index.vue'
import DataCard from '@/framework/components/DataCard/index.vue'
import RecordList from '@/framework/components/DataList/index.vue'
import { getPortalConfig } from '@/framework/apis/portal/config'
import DataFilter from '@/framework/components/PortalData/DataFilter/index.vue'
import DataSorter from '@/framework/components/PortalData/DataSorter/index.vue'
import { addPropertyValueToObject, isEmpty, isNotEmpty } from '@/framework/utils/common'
import { CommonStringObjectType, LabelContentArray } from '@/framework/types/common-data'
import { CARD_POSITION, ConditionListType, FIELD_TYPE, QuerySortType } from '@/framework/types/portal.d'
import AssociationPortal from './AssociationPortal/index.vue'
import { useRefreshStore } from '@/framework/store/layout/refresh'
import { getStaff } from '@/framework/apis/staff'
import {
  CoverPropertyListType,
  getCardBadge,
  getCardLeftBottomImg,
  getCardLeftBottomText,
  getCardRightBottomText,
  getCardSubTitle,
  getCardTags,
  getCardTitle,
  getContentItemList,
  onclickCard
} from './cardFuns'
import { PortalBindType } from '@/framework/components/PortalData/types'
import { Ref } from 'vue'
import Drag from 'vuedraggable'
import EmptyContent from '@/framework/components/EmptyContent/index.vue'
import { name } from '@/../package.json'
import { getNaviBarColor } from '@/framework/utils/e10'
import _ from 'lodash'

let initialized = false
const props = withDefaults(
  defineProps<{
    tableId: string
    baseDomain?: string
    emptyContent?: string
    noHead?: boolean
    detailInfo?: boolean
    associationInfo?: boolean
    coverPropertyList?: CoverPropertyListType[]
    defaultSortColumn?: Array<QuerySortType> | undefined
    bindTabs?: Array<PortalBindType>
    isAssociation?: boolean
    externalSearchCondition?: ConditionListType[]
    activeTab?: any
    tabs?: Array<any>
    tabIdField?: string
    tabNameField?: string
    draggable?: boolean
  }>(),
  {
    baseDomain: '/' + name,
    emptyContent: '暂无数据',
    noHead: false,
    detailInfo: true,
    associationInfo: true,
    coverPropertyList: () => [],
    defaultSortColumn: undefined,
    bindTabs: () => [] as PortalBindType[],
    isAssociation: false,
    externalSearchCondition: undefined,
    activeTab: '0',
    tabs: undefined,
    tabIdField: 'id',
    tabNameField: 'name',
    draggable: false
  }
)

const { externalSearchCondition, tabs, draggable, activeTab } = toRefs(props)
const emit = defineEmits<{
  (e: 'update:activeTab', arg: any): void
  (e: 'click', record: any, originalData: any): void
  (e: 'onTabClick', arg: any): void
}>()
const refreshStore = useRefreshStore()
const showSortDialog = ref(false)
const dataList: Ref<Array<any>> = ref([])
const originalDataMap = new Map()
const config = reactive({
  tableId: props.tableId,
  baseDomain: props.baseDomain,
  total: 100,
  url: '',
  idColumn: '',
  nameColumn: '',
  orderColumn: '',
  defaultSort: [],
  modal: {
    show: false,
    data: {} as { [key: string]: any }
  },
  pageSize: 10
})
const tabRef = ref()
const tabKey = ref(1)
const _active = ref(activeTab.value)
const backgroundColor: Ref<string> = ref('#00549d')
const dict = useDictStore()
const treeDict = useTreeStore()
let showDetail: Ref<boolean> = ref(false)

const onclickCardEvent = (data: any) => onclickCard(data, columnsArrayMap, detailDataList, showDetail)

const queryDataAsync = async () => {
  const resolve = (res: any) => {
    config.total = res.payload.total
    return res.payload.records
  }
  return await generalQuery(config.url, getQueryCondition(), config.baseDomain).then(resolve)
}

const queryCondition = computed(() => {
  return {
    conditionList: externalSearchCondition.value || ([] as ConditionListType[]),
    sortList: props.defaultSortColumn || config.defaultSort || [],
    currentPage: 1,
    pageSize: config.pageSize
  }
})

const _bindTabs: Ref<Array<PortalBindType>> = ref(props.bindTabs || ([] as Array<PortalBindType>))
let detailDataList: Ref<{ title: string; dataList: LabelContentArray }[]> = ref([])

const getQueryCondition = () => {
  return queryCondition.value
}

let columnArray: Ref<any> = ref([])
const columnsArrayMap = {} as any
const columnsReferenceDictMap = {} as CommonStringObjectType
const columnsReferenceTreeDictMap = {} as CommonStringObjectType
const showPositionMap = {} as { [key: string]: Array<any> }

const onDragEnd = () => {
  refreshStore.enableRefresh()
  let updateOrderData: any = []
  dataList.value.forEach((node: any, index: number) => {
    updateOrderData.push({
      id: node[config.idColumn],
      showOrder: index + 1
    })
  })
  updateOrder(config.url, updateOrderData, config.baseDomain).then(() => requestAndRenderData())
}

const init = async () => {
  return await getPortalConfig(config.tableId).then(async (res) => {
    initialized = true
    const tableConfig = res.payload
    config.url = tableConfig.url
    config.idColumn = tableConfig.idColumn
    config.nameColumn = tableConfig.nameColumn
    config.orderColumn = tableConfig.orderColumn
    if (tableConfig.defaultSort) config.defaultSort = JSON.parse(tableConfig.defaultSort)
    let promiseList = []
    if (!columnArray.value.length) {
      for (let layout of tableConfig.columns) {
        const { fieldType, property, mobileDisplayType } = layout
        const referenceDict = layout.reference || layout.entity
        layout['filterValues'] = []
        // layout['filterStrict'] = false
        if (+layout.enable === 1) {
          columnArray.value.push(layout)
          columnsArrayMap[property] = layout
        }
        addPropertyValueToObject(showPositionMap, mobileDisplayType, property)
        if (isNotEmpty(referenceDict)) {
          if (fieldType === FIELD_TYPE.SELECT || fieldType === FIELD_TYPE.SELECT_MULTI_IN_ONE) {
            columnsReferenceDictMap[property] = referenceDict
            let promise = dict.getDict(referenceDict)
            promiseList.push(promise)
          } else if (fieldType === FIELD_TYPE.TREE || fieldType === FIELD_TYPE.TREE_MULTI_IN_ONE) {
            columnsReferenceTreeDictMap[property] = referenceDict
            let promise = treeDict.getTree(referenceDict)
            promiseList.push(promise)
          }
        }
      }
      columnArray.value = [...columnArray.value]
      await Promise.all(promiseList)

      // 关联配置
      if (isNotEmpty(tableConfig.associates) && isEmpty(_bindTabs.value)) {
        for (let associate of tableConfig.associates) {
          const bind: PortalBindType = {} as PortalBindType
          bind.title = associate.title
          bind.tableId = associate.bindPortalName
          bind.bindType = associate.bindType
          if (isNotEmpty(associate.attachCondition)) {
            bind.defaultAdvancedCondition = JSON.parse(associate.attachCondition)
          }
          bind.defaultSortColumn = [
            {
              property: associate.bindSortPrperty,
              type: associate.bindSortType
            }
          ]
          bind.treeMode = associate.treeMode === '1'
          bind.checkStrictly = associate.treeCheckStrict === '1'
          bind.bindFieldProperty = associate.bindProperty
          _bindTabs.value.push(bind)
        }
        _bindTabs.value = [..._bindTabs.value]
      }
    }
    return await queryDataAsync()
  })
}

const processData = async (data: Array<any>) => {
  originalDataMap.set('originalData', data)
  for (const row of data) {
    originalDataMap.set(row[config.idColumn], _.cloneDeep(row))
    for (const key in row) {
      if (Object.prototype.hasOwnProperty.call(columnsArrayMap, key)) {
        const { fieldType, reference } = columnsArrayMap[key]
        if (fieldType === FIELD_TYPE.MONEY) {
          const [precision, ratio] = reference.split(',').map(Number)
          row[key] = (+row[key] / ratio).toFixed(precision)
        }
      }
      if (Object.prototype.hasOwnProperty.call(columnsReferenceDictMap, key)) {
        await dict.getLabelByValue(columnsReferenceDictMap[key], row[key]).then((res) => (row[key] = res))
      } else if (Object.prototype.hasOwnProperty.call(columnsReferenceTreeDictMap, key)) {
        await treeDict.getLabelByValue(columnsReferenceTreeDictMap[key], row[key]).then((res) => (row[key] = res))
      }
    }
  }
  return data
}

let allStaffMap = {} as { [key: string]: any }

const requestAndRenderData = async (isAddData = false, needInit = false) => {
  let getDataPromise
  if (needInit) getDataPromise = init()
  else getDataPromise = queryDataAsync()
  return getDataPromise
    .then((data) => processData(data))
    .then(async (data) => {
      const ownerProperty = showPositionMap[CARD_POSITION.OWNER]
      if (ownerProperty && ownerProperty.length > 0) {
        const key = ownerProperty[0]
        const staffNumberList = data.map((item: any) => item[key])
        await getStaff(staffNumberList, false).then(({ payload }) =>
          payload.forEach((item: any) => (allStaffMap[item.value] = item))
        )
      }
      return data
    })
    .then((data) => {
      if (isAddData) dataList.value.push(...data)
      else dataList.value = data
      if (dataList.value.length >= config.total) {
        finished.value = true
      }
    })
}

const onQueryFilter = (conditionList: ConditionListType[]) => {
  queryCondition.value.conditionList = conditionList
  if (externalSearchCondition.value) queryCondition.value.conditionList.push(...externalSearchCondition.value)
  requestAndRenderData()
}

const onSortData = (sortCondition: Array<QuerySortType>) => {
  queryCondition.value.sortList = sortCondition
  requestAndRenderData()
}

const onViewAssociation = (data: any) => {
  config.modal.show = true
  config.modal.data = data
}

onBeforeMount(() => requestAndRenderData(false, true))
onMounted(() => {
  refreshStore.regRefresh(() => {
    queryCondition.value.currentPage = 1
    requestAndRenderData()
  })
  getNaviBarColor((res: any) => {
    backgroundColor.value = res.barColor
  })
})
watch(
  () => tabs.value,
  () => tabKey.value++,
  {
    deep: true
  }
)
watch(
  () => activeTab.value,
  (newValue, oldValue) => {
    if (newValue !== oldValue) {
      tabKey.value++
      _active.value = activeTab.value
    }
  }
)
watch(
  () => _active.value,
  (newValue, oldValue) => {
    if (newValue !== oldValue) {
      emit('update:activeTab', _active.value)
    }
  }
)
const reLoad = async (condition: ConditionListType[]) => {
  queryCondition.value.conditionList = condition || []
  queryCondition.value.currentPage = 1
  return requestAndRenderData(false, !initialized)
}
defineExpose({ reLoad })
const loading = ref(false)
const finished = ref(false)

const onLoad = () => {
  queryCondition.value.currentPage = queryCondition.value.currentPage + 1
  requestAndRenderData(true).then(() => (loading.value = false))
}
</script>

<style scoped>
.top-toolbar {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  box-sizing: border-box;
  box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.2);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 101;
}

.top-toolbar-holder {
  height: 10vh;
  width: 100%;
}

:deep(.top-toolbar button) {
  margin-left: 3vw;
}

.view-associations-btn {
  height: 100%;
}

.ghost {
  opacity: 0.5;
  border: 1px solid #1818e1;
}

.chosen {
  border: 1px solid #1818e1;
}
</style>
